home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Graphics 2D / Restore Screen Cluts / PictDocument.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  70.8 KB  |  1,945 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        PictDocument.c
  3.  
  4.     Contains:    This source file is the most important part of this program because it shows
  5.                 all the code needed to maintain Picture Documents.  A Picture Document is a
  6.                 window which displays an off-screen image and lets you paint into it, and
  7.                 these two activities show how to use graphics environments to draw off screen
  8.                 and copy the off-screen image to the window on the screen.
  9.  
  10.     Written by: Forrest Tanaka    
  11.  
  12.     Copyright:    Copyright © 1988-1999 by Apple Computer, Inc., All Rights Reserved.
  13.  
  14.                 You may incorporate this Apple sample source code into your program(s) without
  15.                 restriction. This Apple sample source code has been provided "AS IS" and the
  16.                 responsibility for its operation is yours. You are not permitted to redistribute
  17.                 this Apple sample source code as "Apple sample source code" after having made
  18.                 changes. If you're going to re-distribute the source, we require that you make
  19.                 it clear in the source that the code was descended from Apple sample source
  20.                 code, but that you've made changes.
  21.  
  22.     Change History (most recent first):
  23.                 7/13/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  24.                 
  25.  
  26. */
  27.  
  28.  
  29. /******************************************************************************\
  30. * Header Files
  31. \******************************************************************************/
  32.  
  33. /* System header files needed by MPW C */
  34. #ifndef THINK_C
  35. #include <Errors.h>
  36. #include <Menus.h>
  37. #include <Resources.h>
  38. #include <Windows.h>
  39. #endif
  40.  
  41. #include <GestaltEqu.h>
  42. #include <Palettes.h>
  43. #include <ColorPicker.h>
  44. #include <QDOffscreen.h>
  45.  
  46. /* Header files specific to this program */
  47. #include "PictDocument.h"
  48. #include "EmergMem.h"
  49. #include "ColorReset.h"
  50. #include "MenuHandler.h"
  51. #include "WindowPositioner.h"
  52.  
  53. #ifndef topLeft
  54. #define topLeft(r)                      (((Point *) &(r))[0])
  55. #endif
  56.  
  57. #ifndef botRight
  58. #define botRight(r)                     (((Point *) &(r))[1])
  59. #endif
  60.  
  61.  
  62. /******************************************************************************\
  63. * Private Constants
  64. \******************************************************************************/
  65.  
  66. /* Picture Document window constants */
  67. #define rPictDocWindID 128 /* Resource ID of Picture Document WIND resource */
  68. #define rScrollBarID   128 /* Resource ID of scroll bar CNTL resource */
  69. #define kDocWKind      128 /* windowKind field of Picture Document windows */
  70. #define kMinWindowSize 64  /* Minimum size of the window in pixels */
  71.  
  72. /* PICT file constants */
  73. #define kPictFileHeaderSize 512    /* Number of bytes in PICT file header */
  74. #define kFileCreator        'CLIM' /* Creator code for this application */
  75. #define kFileType           'PICT' /* File type for this app’s documents */
  76.  
  77. /* Off-screen Constants */
  78. #define kMaxRowBytes 0x3FFE     /* Maximum number of bytes in a row of pixels */
  79. #define kDefaultRes  0x00480000 /* Default resolution is 72 DPI; Fixed type */
  80. #define kITabRes     4          /* Inverse-table resolution */
  81.  
  82. /* Miscellaneous constants */
  83. #define kPaintRadius 8 /* Radius of a drop of paint in pixels */
  84.  
  85.  
  86. /******************************************************************************\
  87. * Private Types
  88. \******************************************************************************/
  89.  
  90. /* Picture Document information */
  91. typedef struct
  92. {
  93.     GWorldPtr     image;      /* Pointer to image in off-screen GWorld */
  94.     Rect          windowRect; /* Rectangle of window except for scroll bars */
  95.     Rect          destRect;   /* Rectangle of image in window */
  96.     RGBColor      foreground; /* Color of foreground */
  97.     RGBColor      background; /* Color of background */
  98.     ControlHandle vScrollBar; /* Horizontal scroll bar */
  99.     ControlHandle hScrollBar; /* Vertical scroll bar */
  100.     FSSpec        file;       /* File spec of Picture Document’s PICT file */
  101.     short         fileRef;    /* File ref # of Picture Document’s PICT file */
  102.     Boolean       dithering;  /* True if image should be dithered */
  103. } PictDocInfoRec, *PictDocInfoPtr, **PictDocInfoHnd;
  104.  
  105. /* Basic picture information */
  106. typedef struct
  107. {
  108.     CTabHandle colors; /* Handle to color table of deepest pixel map */
  109.     short      depth;  /* Max depth for all pixmaps (in the picture) */
  110.     Rect       frame;  /* Picture frame rect (contains entire picture) */
  111. } PictureInfoRec;
  112.  
  113.  
  114. /******************************************************************************\
  115. * Prototypes of Private Functions
  116. \******************************************************************************/
  117.  
  118. static OSErr CreatePictDoc(
  119.     Rect       *pictureRect,
  120.     short      pictureDepth,
  121.     CTabHandle pictureClut,
  122.     WindowPtr  *retDoc);
  123.  
  124.  
  125. static void ScrollPictDoc(
  126.     WindowPtr docWindow,
  127.     short     hScrollDelta,
  128.     short     vScrollDelta);
  129.  
  130. void ResizePictDoc(
  131.     WindowPtr docWindow);
  132.  
  133. OSErr CreateOffScreen(
  134.     Rect       *bounds,
  135.     short      depth,
  136.     CTabHandle colors,
  137.     CGrafPtr   *retPort,
  138.     GDHandle   *retGDevice);
  139.  
  140. OSErr SetUpPixMap(
  141.     short        depth,
  142.     Rect         *bounds,
  143.     CTabHandle   colors,
  144.     short        bytesPerRow,
  145.     PixMapHandle aPixMap);
  146.  
  147. OSErr CreateGDevice(
  148.     PixMapHandle basePixMap,
  149.     GDHandle     *retGDevice);
  150.  
  151. void DisposeOffScreen(
  152.     CGrafPtr doomedPort,
  153.     GDHandle doomedGDevice);
  154.  
  155. OSErr GetPictureInfo(
  156.     short          pictFileRef,
  157.     PictureInfoRec *retPictInfo);
  158.  
  159.  
  160. OSErr DrawFilePicture(
  161.     short pictFileRef,
  162.     Rect  *destRect);
  163.  
  164.  
  165. OSErr OpenFilePicture(
  166.     short pictFileRef,
  167.     Rect  *sourceRect);
  168.  
  169. OSErr CloseFilePicture(void);
  170.  
  171.  
  172.  
  173. /******************************************************************************\
  174. * Global Variables
  175. \******************************************************************************/
  176.  
  177. PictureInfoRec gPictureInfo;  /* Information about a PICT file */
  178. PicHandle      gSpoolPicture; /* Temporary picture used to spool from PICT file */
  179. QDProcsPtr     gSavedProcs;   /* Saves existing QDProcs; used when spooling out */
  180. CQDProcs       gCustomProcs;  /* Customized bottlenecks to spool to PICT file */
  181. short          gPictFileRef;  /* File reference number of a PICT file */
  182. short          gPictureSize;  /* Size of picture in bytes */
  183.  
  184.  
  185. /******************************************************************************\
  186. * NAME & SYNOPSIS:
  187. * FindPictDoc: Find the Picture Document window for a specific file
  188. *
  189. * PARAMETERS:
  190. * See PictDocument.h
  191. *
  192. * DEFINITION:
  193. * See PictDocument.h
  194. *
  195. * DESCRIPTION:
  196. * Each Picture Document window in the window list is examined to see whether it
  197. * has a corresponding disk file.  The fileRef field of the Picture Document
  198. * information record is non-zero if the Picture Document has a disk file
  199. * associated with it.  If there is a disk file, then EqualFSSpec is called to
  200. * see if the FSSpec that I saved in the document’s info record is the same as
  201. * the one passed in fileSpec.  If they are, then a pointer to the window is
  202. * returned.  Otherwise, the loop goes to the next Picture Document window, if
  203. * there is one.
  204. *
  205. * See ColorReset.h for a definition of EqualFSSpec.
  206. *
  207. * RETURN VALUES:
  208. * See PictDocument.h
  209. \******************************************************************************/
  210.  
  211. WindowPtr FindPictDoc(
  212.     FSSpecPtr fileSpec) /* Specification of file to search for */
  213. {
  214.     WindowPtr      pictDocWindow; /* Pointer to Picture Document being tested */
  215.     PictDocInfoHnd pictDocInfo;   /* Handle to Picture Document info rec */
  216.     Boolean        foundDoc;      /* True if found Pict Doc w/ same FSSpec */
  217.  
  218.     /* Get a pointer to the first Picture Document window in the window list */
  219.     pictDocWindow = NextPictDocWindow( nil );
  220.  
  221.     /* Keep searching until found or at end of list */
  222.     foundDoc = false;
  223.     while (pictDocWindow != nil && !foundDoc)
  224.     {
  225.         /* Only have to check FSSpecs if document has a corresponding file */
  226.         pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  227.         if ((**pictDocInfo).fileRef != 0)
  228.         {
  229.             /* See if the Picture Document’s file is same as fileSpec */
  230.             HLock( (Handle)pictDocInfo );
  231.             foundDoc = EqualFSSpec( fileSpec, &(**pictDocInfo).file );
  232.             HUnlock( (Handle)pictDocInfo );
  233.  
  234.             /* If FSSpecs aren’t the same, then go to next Picture Document */
  235.             if (!foundDoc)
  236.                 pictDocWindow = NextPictDocWindow( pictDocWindow );
  237.         }
  238.     }
  239.  
  240.     return pictDocWindow;
  241. }
  242.  
  243.  
  244. /******************************************************************************\
  245. * NAME & SYNOPSIS:
  246. * NextPictDocWindow: Return a pointer to the next Picture Document window
  247. *
  248. * PARAMETERS:
  249. * See PictDocument.h
  250. *
  251. * DEFINITION:
  252. * See PictDocument.h
  253. *
  254. * DESCRIPTION:
  255. * NextPictDocWindow goes through every window in the window list that’s behind
  256. * the window specified by aWindow until either a Picture Document window is
  257. * found or until the end of the window list is reached.  If aWindow is nil, then
  258. * the search starts with the front window.
  259. *
  260. * RETURN VALUES:
  261. * See PictDocument.h
  262. \******************************************************************************/
  263.  
  264. WindowPtr NextPictDocWindow(
  265.     WindowPtr aWindow) /* Window to start search from, or nil if front */
  266. {
  267.     /* Start search from next window, or front window if aWindow is nil */
  268.     if (aWindow == nil)
  269.         aWindow = FrontWindow();
  270.     else
  271.         aWindow = (WindowPtr)((WindowPeek)aWindow)->nextWindow;
  272.  
  273.     /* Search until Picture Document wind found or end of wind list reached */
  274.     while (aWindow != nil && !IsPictDocWindow( aWindow ))
  275.         aWindow = (WindowPtr)((WindowPeek)aWindow)->nextWindow;
  276.  
  277.     return aWindow;
  278. }
  279.  
  280.  
  281. /******************************************************************************\
  282. * NAME & SYNOPSIS:
  283. * IsPictDocWindow: Is a window a Picture Document window?
  284. *
  285. * PARAMETERS:
  286. * See PictDocument.h
  287. *
  288. * DEFINITION:
  289. * See PictDocument.h
  290. *
  291. * DESCRIPTION:
  292. * When I create a window, I store a code for that kind of window in the
  293. * windowKind field.  IsPictDocWindow checks the windowKind field of the given
  294. * window against the constant kDocWKind.  If they’re equal, then aWindow
  295. * must be a Picture Document window.
  296. *
  297. * RETURN VALUES:
  298. * See PictDocument.h
  299. \******************************************************************************/
  300.  
  301. Boolean IsPictDocWindow (
  302.     WindowPtr aWindow) /* Pointer to the window being tested */
  303. {
  304.     return ((WindowPeek)aWindow)->windowKind == kDocWKind;
  305. }
  306.  
  307.  
  308. /******************************************************************************\
  309. * NAME & SYNOPSIS:
  310. * DrawPictDoc: Draw the contents of a Picture Document
  311. *
  312. * PARAMETERS:
  313. * See PictDocument.h
  314. *
  315. * DEFINITION:
  316. * See PictDocument.h
  317. *
  318. * DESCRIPTION:
  319. * The image for Picture Documents is stored in an off-screen GrafPort, and its
  320. * GrafPtr is stored in the information record.  This GrafPtr is retrieved, and
  321. * it’s used to CopyBits the GrafPort to the window.  It’s possible that not all
  322. * of the image can be copied to the window.  For example, part of the window
  323. * might be covered by another window.  In this case, the visRgn of the window
  324. * takes care of clipping out the undesireable parts of the original image.  The
  325. * other case in which part of the image shouldn’t be drawn is if part of the
  326. * image is scrolled out of the window.  The visRgn of the window takes care of
  327. * part of this, but we still have to make sure that the image doesn’t draw
  328. * itself over the scroll bar areas of the window in case the scroll bars are
  329. * hidden, which they are if the window is inactive.  To take care of this, the
  330. * destination rectangle which is the rectangle that the off-screen image is
  331. * copied to, is first intersected with the window rectangle, which is the
  332. * rectangle of the window except for the scroll bars.  This is used as the
  333. * destination rectangle to CopyBits.  Then this rectangle is offset by the top-
  334. * left corner of the original destination rectangle, and this is used as the
  335. * source rectangle to CopyBits.  The off-screen image is then copied to the
  336. * window.
  337. *
  338. * RETURN VALUES:
  339. * See PictDocument.h
  340. \******************************************************************************/
  341.  
  342. void DrawPictDoc(
  343.     WindowPtr docWindow) /* Ptr to Picture Document window being drawn */
  344. {
  345.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document information rec */
  346.     GWorldPtr      image;       /* Pointer to the off-screen image */
  347.     Rect           destRect;    /* Rectangle to copy image into */
  348.     Rect           sourceRect;  /* Rectangle to copy image from */
  349.     Rect           windowRect;  /* Rect of the window except for scroll bars */
  350.     short          mode;        /* Drawing mode to use */
  351.  
  352.     /* Get a pointer to the off-screen image and the window rectangle */
  353.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  354.     image = (**pictDocInfo).image;
  355.     windowRect = (**pictDocInfo).windowRect;
  356.  
  357.     /* Calculate the rectangle to copy the image to */
  358.     destRect = (**pictDocInfo).destRect;
  359.     (void)SectRect( &destRect, &windowRect, &destRect );
  360.  
  361.     /* Calculate the rectangle to copy the image from */
  362.     sourceRect = destRect;
  363.     OffsetRect( &sourceRect, -(**pictDocInfo).destRect.left,
  364.             -(**pictDocInfo).destRect.top );
  365.  
  366.     /* Specify the drawing mode to use */
  367.     mode = srcCopy;
  368.     if ((**pictDocInfo).dithering)
  369.         mode |= ditherCopy;
  370.  
  371.     /* Draw the off-screen image to the screen */
  372.     EraseRect( &docWindow->portRect );
  373.     CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  374.             &sourceRect, &destRect,
  375.             mode, nil );
  376.  
  377.     /* Draw the grow box */
  378.     DrawGrowIcon( docWindow );
  379.  
  380.     /* Draw the scroll bars */
  381.     DrawControls( docWindow );
  382. }
  383.  
  384.  
  385. /******************************************************************************\
  386. * NAME & SYNOPSIS:
  387. * ClickPictDoc: Handle a mouse click in a Picture Document window
  388. *
  389. * PARAMETERS:
  390. * See PictDocument.h
  391. *
  392. * DEFINITION:
  393. * See PictDocument.h
  394. *
  395. * DESCRIPTION:
  396. * The first really important call in this routine is FindControl, which is the
  397. * toolbox call which tells you which control and which part of the control the
  398. * mouse was clicked in.  If it returns zero, then the mouse was clicked outside
  399. * of any control, and so we let the user paint into the document.
  400. *
  401. * Painting into the document is done within a loop which iterates as long as the
  402. * mouse button is held down.  While this is the case, the current mouse position
  403. * is retrieved with a call to GetMouse.  If the mouse position changed from the
  404. * previous iteration’s mouse position, then the mouse position is offset to take
  405. * into account the currently-scrolled position, and then the current port is set
  406. * to the document’s off-screen GrafPort.  Then, PaintOval is called which draws
  407. * a drop of paint into the off-screen GrafPort.
  408. *
  409. * The off-screen image then has to be copied to the window so that the user can
  410. * see the new drop of paint.  This is done by first seeing if the drop of paint
  411. * can actually be seen in the window in the currently-scrolled position.  If it
  412. * can, then the rectangle of the drop of paint is offset to take into account
  413. * the currently-scrolled position.  Then the current port is set to the document
  414. * window, and CopyBits is called to copy the new drop of paint to the document
  415. * window.
  416. *
  417. * Finally, the mouse position is remembered and the loop iterates again if the
  418. * mouse button is still held down.
  419. *
  420. * If FindControl indicates that the mouse click was in the thumb of a scroll
  421. * bar, TrackControl is called which lets the user move the thumb around until
  422. * the user releases the mouse button.  Then, the difference between the old and
  423. * new scroll bar position is calculated which indicates the number of pixels
  424. * that the document must be scrolled.  ScrollPictDoc, defined below, is then
  425. * called to scroll the document’s image, update the scrolled-in part of the
  426. * image, and update the document information record to reflect the new scrolled
  427. * position.
  428. *
  429. * If FindControl indicates that the mouse click was in the up arrow, down arrow,
  430. * page up, or page down areas of either scroll bar, then TrackControl is called
  431. * to scroll the document appropriately.  The ScrollActionProc routine, defined
  432. * below, handles the logistics of scrolling in this case.
  433. *
  434. * RETURN VALUES:
  435. * See PictDocument.h
  436. \******************************************************************************/
  437.  
  438. void ClickPictDoc(
  439.     WindowPtr   docWindow,   /* Picture Document window that was clicked */
  440.     EventRecord *clickEvent) /* Mouse click event */
  441. {
  442.     PictDocInfoHnd pictDocInfo;     /* Handle to Picture Document info rec */
  443.     GWorldPtr      image;           /* Pointer to the off-screen image */
  444.     GDHandle       savedDevice;     /* Saves default GDevice */
  445.     Rect           paintRect;       /* Rectangle to draw paint into */
  446.     Rect           windowPaintRect; /* Rectangle to draw paint into */
  447.     Rect           windowRect;      /* Window rect except for scroll bars */
  448.     Rect           destRect;        /* Rectangle of image in window */
  449.     Point          newPosition;     /* New pos of mouse click; local coords */
  450.     Point          oldPosition;     /* Old pos of mouse click; local coords */
  451.     ControlHandle  scrollBar;       /* Handle to the clicked scroll bar */
  452.     short          partNumber;      /* Part # of clicked part of control */
  453.     short          scrollDelta;     /* Number of pixels to scroll */
  454.  
  455.     savedDevice = GetGDevice();
  456.  
  457.     /* Get information about the Picture Document */
  458.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  459.     image = (**pictDocInfo).image;
  460.     windowRect = (**pictDocInfo).windowRect;
  461.     destRect = (**pictDocInfo).destRect;
  462.  
  463.     /* Get the mouse click position */
  464.     SetPort( docWindow );
  465.     newPosition = clickEvent->where;
  466.     GlobalToLocal( /*◊*/&newPosition );
  467.  
  468.     /* Find whether the mouse was in a control */
  469.     partNumber = FindControl( newPosition, docWindow, /*<*/&scrollBar );
  470.     switch (partNumber)
  471.     {
  472.         case 0:
  473.             /* Clicked outside of any control; see if it was in the image */
  474.             if (PtInRect( newPosition, &windowRect ))
  475.             {
  476.                 /* Click in image; paint while mouse button is held down */
  477.                 oldPosition.v = oldPosition.h = -32767;
  478.                 while (StillDown())
  479.                 {
  480.                     /* Only do something if the mouse moved */
  481.                     if (!EqualPt( newPosition, oldPosition ))
  482.                     {
  483.                         /* Need to offset paint brush for scrolled position */
  484.                         SubPt( topLeft( destRect ), /*◊*/&newPosition );
  485.                         SetRect( /*<*/&paintRect, newPosition.h - kPaintRadius,
  486.                                 newPosition.v - kPaintRadius,
  487.                                 newPosition.h + kPaintRadius,
  488.                                 newPosition.v + kPaintRadius );
  489.  
  490.                         /* Drop some paint into the off-screen image */
  491.                         SetGWorld( image, nil );
  492.                         PaintOval( &paintRect );
  493.  
  494.                         /* Offset brush back to window position */
  495.                         AddPt( topLeft( destRect ), /*◊*/&newPosition );
  496.  
  497.                         /* Calc rect in window for to brush rect in image */
  498.                         windowPaintRect = paintRect;
  499.                         OffsetRect( &windowPaintRect, destRect.left,
  500.                                 destRect.top );
  501.  
  502.                         /* Only copy if some part of brush within image */
  503.                         if (SectRect( &windowPaintRect, &windowRect,
  504.                                 &windowPaintRect ))
  505.                         {
  506.                             /* Make clipped paint rect in off-screen image */
  507.                             paintRect = windowPaintRect;
  508.                             OffsetRect( &paintRect, -destRect.left,
  509.                                     -destRect.top );
  510.  
  511.                             /* Copy painted off-screen image to the screen */
  512.                             SetGWorld( (CGrafPtr)docWindow, nil );
  513.                             CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  514.                                     &paintRect, &windowPaintRect,
  515.                                     srcCopy, nil );
  516.                         }
  517.  
  518.                         /* Remember the new mouse position */
  519.                         oldPosition = newPosition;
  520.                     }
  521.  
  522.                     /* Get the new position of the mouse */
  523.                     SetPort( docWindow );
  524.                     GetMouse( /*<*/&newPosition );
  525.                 }
  526.             }
  527.             break;
  528.         case kControlIndicatorPart:
  529.             /* Mouse in thumb of scroll bar; get existing control value */
  530.             scrollDelta = GetControlValue( scrollBar );
  531.  
  532.             /* Track mouse in thumb until mouse button is released */
  533.             partNumber = TrackControl( scrollBar, newPosition, nil );
  534.             if (partNumber != 0)
  535.             {
  536.                 /* Find diff between old and new scroll bar values */
  537.                 scrollDelta -= GetControlValue( scrollBar );
  538.                 if (scrollDelta != 0)
  539.                 {
  540.                     if (scrollBar == (**pictDocInfo).vScrollBar)
  541.                         ScrollPictDoc( docWindow, 0, scrollDelta );
  542.                     else
  543.                         ScrollPictDoc( docWindow, scrollDelta, 0 );
  544.                 }
  545.             }
  546.             break;
  547.         case kControlUpButtonPart:
  548.         case kControlDownButtonPart:
  549.         case kControlPageUpPart:
  550.         case kControlPageDownPart:
  551.             TrackControl( scrollBar, newPosition, gActionProc);
  552.             break;
  553.         default:
  554.             break;
  555.     }
  556. }
  557.  
  558.  
  559. /******************************************************************************\
  560. * NAME & SYNOPSIS:
  561. * ScrollActionProc: Handle a click in a scroll bar (except for thumb)
  562. *
  563. * PARAMETERS:
  564. * ControlHandle control: Control that received the mouse click
  565. * short         part:    Part number of the clicked control part
  566. *
  567. * DEFINITION:
  568. * ScrollActionProc is called by the Control Manager when TrackControl is called
  569. * in the ClickPictDoc function above.  It handles scrolling when the user clicks
  570. * in the scroll arrows or page areas of the scroll bar.  The document is
  571. * scrolled by the appropriate amount.
  572. *
  573. * DESCRIPTION:
  574. * ScrollActionProc first checks to see which part of the scroll bar was clicked.
  575. * If it was in either of the arrows, then the desired scroll amount is one
  576. * pixel.  If it was in either of the paging areas, then the desired scroll
  577. * amount is 32 pixels.  The current value of the clicked scroll bar is then
  578. * adjusted by the desired scroll amount, and the result is compared against the
  579. * limits of the scroll bar’s values.  If the new value is beyond either limit,
  580. * then it’s pinned back into the limits.  Then the real amount to scroll is
  581. * calculated simply by subtracting the new scroll value from the old scroll
  582. * value, and ScrollPictDoc (defined below) is called to scroll the Picture
  583. * Document window.
  584. *
  585. * RETURN VALUES:
  586. * None
  587. \******************************************************************************/
  588.  
  589. pascal  void ScrollActionProc(
  590.     ControlHandle control, /* Handle to clicked control */
  591.     short         part)    /* Part number of clicked control part */
  592. {
  593.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document info rec */
  594.     WindowPtr      docWindow;   /* Clicked Picture Document window */
  595.     Rect           windowRect;  /* Window rect except for scroll bars */
  596.     Rect           destRect;    /* Rectangle of image in window */
  597.     short          scrollDelta; /* Number of pixels to scroll by */
  598.     short          oldValue;    /* Value of scroll bar before scrolling */
  599.     short          newValue;    /* Value of scroll bar after scrolling */
  600.     short          maxValue;    /* Maximum value of the scroll bar */
  601.  
  602.     /* Get information about the Picture Document */
  603.     docWindow = (**control).contrlOwner;
  604.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  605.     windowRect = (**pictDocInfo).windowRect;
  606.     destRect = (**pictDocInfo).destRect;
  607.  
  608.     /* Only process if the part # isn’t zero */
  609.     if (part != 0)
  610.     {
  611.         /* Decide scroll amount based on part of control that was clicked */
  612.         switch (part)
  613.         {
  614.             case kControlUpButtonPart:
  615.                 scrollDelta = 1;
  616.                 break;
  617.             case kControlDownButtonPart:
  618.                 scrollDelta = -1;
  619.                 break;
  620.             case kControlPageUpPart:
  621.                 scrollDelta = 32;
  622.                 break;
  623.             case kControlPageDownPart:
  624.                 scrollDelta = -32;
  625.                 break;
  626.             default:
  627.                 scrollDelta = 0;
  628.                 break;
  629.         }
  630.  
  631.         /* Only do something if we’re actually scrolling */
  632.         if (scrollDelta != 0)
  633.         {
  634.             /* Get the current scroll bar state */
  635.             maxValue = GetControlMaximum( control );
  636.             oldValue = GetControlValue( control );
  637.  
  638.             /* Calculate the new scroll bar value pinned to the limits */
  639.             newValue = oldValue - scrollDelta;
  640.             if (newValue < 0)
  641.                 newValue = 0;
  642.             else if (newValue > maxValue)
  643.                 newValue = maxValue;
  644.  
  645.             /* Only scroll if the old and new values are different */
  646.             if (oldValue != newValue)
  647.             {
  648.                 /* Set the new value of the scroll bar */
  649.                 SetControlValue( control, newValue );
  650.  
  651.                 /* Scroll by difference between old and new scroll bar values */
  652.                 scrollDelta = oldValue - newValue;
  653.                 if (control == (**pictDocInfo).vScrollBar)
  654.                     ScrollPictDoc( docWindow, 0, scrollDelta );
  655.                 else
  656.                     ScrollPictDoc( docWindow, scrollDelta, 0 );
  657.             }
  658.         }
  659.     }
  660. }
  661.  
  662.  
  663. /******************************************************************************\
  664. * NAME & SYNOPSIS:
  665. * ScrollPictDoc: Scroll a Picture Document window’s image
  666. *
  667. * PARAMETERS:
  668. * WindowPtr docWindow:    Pointer to Picture Document window to scroll
  669. * short     vScrollDelta: Number of pixels to scroll vertically
  670. * short     hScrollDelta: Number of pixels to scroll horizontally
  671. *
  672. * DEFINITION:
  673. * ScrollPictDoc scrolls the picture document window specified by the docWindow
  674. * parameter by vScrollDelta pixels vertically and hScrollDelta pixels
  675. * horizontally.  The part of the window that’s scrolled in is updated, so no
  676. * updating is needed outside of ScrollPictDoc.
  677. *
  678. * DESCRIPTION:
  679. * ScrollRect is called to scroll the Picture Document’s image by the specified
  680. * amount.  The scrolled-in part is then redrawn with a call to CopyBits.  The
  681. * destRect field of the Picture Document information record keeps track of the
  682. * scrolled position of the image, so it’s updated to reflect the new scrolled
  683. * position.
  684. *
  685. * RETURN VALUES:
  686. * None
  687. \******************************************************************************/
  688.  
  689. static void ScrollPictDoc(
  690.     WindowPtr docWindow,    /* Pointer to document window to scroll */
  691.     short     hScrollDelta, /* Number of pixels to scroll horizontally */
  692.     short     vScrollDelta) /* Number of pixels to scroll vertically */
  693. {
  694.     PictDocInfoHnd pictDocInfo;  /* Handle to Picture Document info rec */
  695.     RgnHandle      updateRegion; /* Region covering scrolled-in area */
  696.     Rect           windowRect;   /* Rect of window except for scroll bars */
  697.     Rect           destRect;     /* New destination rectangle */
  698.     GWorldPtr      image;        /* Off-screen document image */
  699.  
  700.     /* Get the window rectangle and destination rectangle */
  701.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  702.     windowRect = (**pictDocInfo).windowRect;
  703.     destRect = (**pictDocInfo).destRect;
  704.     image = (**pictDocInfo).image;
  705.  
  706.     /* Scroll the image by the specified amount */
  707.     updateRegion = NewRgn();
  708.     ScrollRect( &windowRect, hScrollDelta, vScrollDelta, updateRegion );
  709.  
  710.     /* Update the destination rectangle to the new scrolled position */
  711.     OffsetRect( &destRect, hScrollDelta, vScrollDelta );
  712.     (**pictDocInfo).destRect = destRect;
  713.  
  714.     /* Update the scrolled-in part of the window */
  715.     CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  716.             &image->portRect, &destRect,
  717.             srcCopy, updateRegion );
  718.  
  719.     /* Get rid of the update region */
  720.     DisposeRgn( updateRegion );
  721. }
  722.  
  723.  
  724. /******************************************************************************\
  725. * NAME & SYNOPSIS:
  726. * GrowPictDoc: Let the user change the size of a Picture Document window
  727. *
  728. * PARAMETERS:
  729. * See PictDocument.h
  730. *
  731. * DEFINITION:
  732. * See PictDocument.h
  733. *
  734. * DESCRIPTION:
  735. * GrowWindow is called with a boundary rectangle that specifies that the window
  736. * has a minimum size of kMinWindowSize (defined at the top of this source file)
  737. * and a maximum size that’s the size of the image.  After the user releases the
  738. * mouse button, SizeWindow is called to change the size of the window, and
  739. * ResizePictDoc (defined below) is called to update the Picture Document to the
  740. * new size.
  741. *
  742. * RETURN VALUES:
  743. * See PictDocument.h
  744. \******************************************************************************/
  745.  
  746. void GrowPictDoc(
  747.     WindowPtr   docWindow,   /* Picture Document window that’s to be grown */
  748.     EventRecord *clickEvent) /* Mouse down event */
  749. {
  750.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document info rec */
  751.     Rect           growBounds;  /* Window can be dragged over this rectangle */
  752.     Point          newSize;     /* New size of the new window */
  753.  
  754.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  755.  
  756.     growBounds = (**pictDocInfo).image->portRect;
  757.     growBounds.left = growBounds.top = kMinWindowSize;
  758.     growBounds.right += kScrollBarWidth;
  759.     growBounds.bottom += kScrollBarWidth;
  760.  
  761.     /* Let the user resize the window to any size */
  762.     *((long *)&newSize) = GrowWindow( docWindow, clickEvent->where,
  763.             &growBounds );
  764.  
  765.     /* If the user actually changed the size of the window, resize the window */
  766.     if (*((long *)&newSize) != 0)
  767.     {
  768.         SizeWindow( docWindow, newSize.h, newSize.v, true );
  769.         ResizePictDoc( docWindow );
  770.     }
  771. }
  772.  
  773.  
  774. /******************************************************************************\
  775. * NAME & SYNOPSIS:
  776. * ResizePictDoc: Update a picture document window to a new size
  777. *
  778. * PARAMETERS:
  779. * WindowPtr docWindow: Picture document window that was resized
  780. *
  781. * DEFINITION:
  782. * ResizePictDoc updates the Picture Document window specified by the docWindow
  783. * to a new size.  This routine is called right after the size of docWindow has
  784. * been changed.  The act of updating the Picture Document window involves moving
  785. * the scroll bars to the edge of the window and scrolling the document image.
  786. * The image has to be scrolled when the window is grown in case the bottom or
  787. * right edge of the window goes below or to the right of the image, in which
  788. * case the bottom or right edges of the image has to “chase” the bottom or right
  789. * edges of the window, and that means scrolling.
  790. *
  791. * DESCRIPTION:
  792. * The first major thing that’s done is a new destination rectangle is calculated
  793. * in case the document has to be scrolled as described in the definition.  There
  794. * are three conditions that have to be met before scrolling can happen:
  795. *     1) The upper-left corner of destRect must not be (0,0), meaning that the
  796. *        document’s image is scrolled from its original position.
  797. *     2) The window has become bigger.
  798. *     3) The window’s rectangle has moved below or to the right of the image in
  799. *        the window.
  800. * If all three of these conditions are met for either the horizontal or
  801. * vertical cases, a new destRect is calculated.  If this new destRect is
  802. * different from the old destRect, ScrollPictDoc (defined above) is called to
  803. * scroll the image to the new position.
  804. *
  805. * After this, the windowRect field of the Picture Document information record
  806. * is updated to the new size of the window.  Then, the maximum values of the
  807. * scroll bars are set to the number of pixels in the horizontal or vertical
  808. * direction that can’t be seen.  And then, the old position of the scroll icon
  809. * is invalidated so that it’ll be erased with the document’s image the next time
  810. * the window is updated.
  811. *
  812. * Both of the scroll bars are then moved along the bottom and right edges of the
  813. * window, and the new position of the grow icon is invalidated so that it’s
  814. * redrawn in case the window was made smaller.  Finally, the rectangles of the
  815. * scroll bars are validated so that they won’t be redrawn in the next update
  816. * event.
  817. *
  818. * RETURN VALUES:
  819. * None
  820. \******************************************************************************/
  821.  
  822. static void ResizePictDoc(
  823.     WindowPtr docWindow) /* Picture document window that was resized */
  824. {
  825.     PictDocInfoHnd pictDocInfo;   /* Handle to Picture Document information rec */
  826.     ControlHandle  vScrollBar;    /* Vertical scroll bar */
  827.     ControlHandle  hScrollBar;    /* Horizontal scroll bar */
  828.     Rect           windowRect;    /* Covers part of window that contains image */
  829.     Rect           newWindowRect; /* Covers all of window except scroll bars */
  830.     Rect           scrollRect;    /* Rectangle of scroll bar */
  831.     Rect           destRect;      /* Rectangle of to draw image into */
  832.     Rect           newDestRect;   /* Rectangle of to draw image into */
  833.     CGrafPtr       image;         /* Off-screen document image */
  834.     short          remaining;     /* # rows, columns of pixels not visible */
  835.  
  836.     SetPort( docWindow );
  837.  
  838.     /* Get the Picture Document’s information */
  839.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  840.     vScrollBar = (**pictDocInfo).vScrollBar;
  841.     hScrollBar = (**pictDocInfo).hScrollBar;
  842.     image = (**pictDocInfo).image;
  843.     destRect = (**pictDocInfo).destRect;
  844.     windowRect = (**pictDocInfo).windowRect;
  845.  
  846.     /* Find the new window document area */
  847.     newWindowRect = docWindow->portRect;
  848.     newWindowRect.right -= kScrollBarWidth - 1;
  849.     newWindowRect.bottom -= kScrollBarWidth - 1;
  850.  
  851.     /* Find new destRect in case window growing causes scrolling */
  852.     newDestRect = destRect;
  853.     if (newDestRect.left != 0 && 
  854.             (newWindowRect.right - newWindowRect.left) >
  855.             (windowRect.right - windowRect.left) &&
  856.             newWindowRect.right > newDestRect.right)
  857.         OffsetRect( /*◊*/&newDestRect, (newWindowRect.right - windowRect.right)
  858.                 - (newDestRect.right - windowRect.right), 0 );
  859.     if (newDestRect.top != 0 && 
  860.             (newWindowRect.bottom - newWindowRect.top) >
  861.             (windowRect.bottom - windowRect.top) &&
  862.             newWindowRect.bottom > newDestRect.bottom)
  863.         OffsetRect( /*◊*/&newDestRect, 0, (newWindowRect.bottom -
  864.                 windowRect.bottom) - (newDestRect.bottom - windowRect.bottom) );
  865.  
  866.     /* If the new scrolled position has changed, update the window’s image */
  867.     if (!EqualRect( &newDestRect, &destRect))
  868.         ScrollPictDoc( docWindow, newDestRect.left - destRect.left,
  869.                 newDestRect.top - destRect.top );
  870.  
  871.     /* Set up the rectangles for scroll bar calculations */
  872.     (**pictDocInfo).windowRect = newWindowRect;
  873.  
  874.     /* Calc # rows of image not visible; set as max value of scroll bar */
  875.     remaining = image->portRect.bottom - newWindowRect.bottom;
  876.     if (remaining < 0)
  877.         remaining = 0;
  878.     SetControlMaximum( vScrollBar, remaining );
  879.  
  880.     /* Calc # cols of image not visible; set as max value of scroll bar */
  881.     remaining = image->portRect.right - newWindowRect.right;
  882.     if (remaining < 0)
  883.         remaining = 0;
  884.     SetControlMaximum( hScrollBar, remaining );
  885.  
  886.     /* Invalidate the old position of the grow icon */
  887.     SetRect( &scrollRect, (**hScrollBar).contrlRect.right,
  888.             (**vScrollBar).contrlRect.bottom,
  889.             (**vScrollBar).contrlRect.right,
  890.             (**hScrollBar).contrlRect.bottom );
  891.     InvalRect( &scrollRect );
  892.  
  893.     /* Move the scroll bars along the edges of the window */
  894.     HideControl( vScrollBar );
  895.     HideControl( hScrollBar );
  896.     MoveControl( vScrollBar, newWindowRect.right, newWindowRect.top - 1 );
  897.     MoveControl( hScrollBar, newWindowRect.left - 1, newWindowRect.bottom );
  898.     SizeControl( vScrollBar, kScrollBarWidth, newWindowRect.bottom -
  899.             newWindowRect.top + 2 );
  900.     SizeControl( hScrollBar, newWindowRect.right - newWindowRect.left + 2,
  901.             kScrollBarWidth );
  902.     ShowControl( vScrollBar );
  903.     ShowControl( hScrollBar );
  904.  
  905.     /* Invalidate the new position of the grow icon */
  906.     SetRect( &scrollRect, (**hScrollBar).contrlRect.right,
  907.             (**vScrollBar).contrlRect.bottom,
  908.             (**vScrollBar).contrlRect.right,
  909.             (**hScrollBar).contrlRect.bottom );
  910.     InvalRect( &scrollRect );
  911.  
  912.     /* Validate the new scroll bar areas */
  913.     scrollRect = (**vScrollBar).contrlRect;
  914.     ValidRect( &scrollRect );
  915.     scrollRect = (**hScrollBar).contrlRect;
  916.     ValidRect( &scrollRect );
  917. }
  918.  
  919.  
  920. /******************************************************************************\
  921. * NAME & SYNOPSIS:
  922. * ActivatePictDoc: Activate or deactivate a Picture Document window
  923. *
  924. * PARAMETERS:
  925. * See PictDocument.h
  926. *
  927. * DEFINITION:
  928. * See PictDocument.h
  929. *
  930. * DESCRIPTION:
  931. * If the Picture Document window is becoming active, the scroll bars are shown.
  932. * If it’s becoming inactive, the scroll bars are hidden.  Because hiding
  933. * controls causes the control rectangle to be invalidated, ActivatePictDoc
  934. * validates them again to avoid a very slight flicker in the lines that delimit
  935. * the scroll bar areas.
  936. *
  937. * DrawGrowIcon is called to show or hide the grow icon.  The Window Manager
  938. * automatically decides whether to show or hide the grow icon.
  939. *
  940. * RETURN VALUES:
  941. * See PictDocument.h
  942. \******************************************************************************/
  943.  
  944. void ActivatePictDoc(
  945.     WindowPtr docWindow,      /* Pointer to Doc window being updated */
  946.     Boolean   becomingActive) /* True if Doc wind is becoming active */
  947. {
  948.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document information rec */
  949.     ControlHandle  vScrollBar;  /* Vertical scroll bar */
  950.     ControlHandle  hScrollBar;  /* Horizontal scroll bar */
  951.     Rect           scrollRect;  /* Rectangle of the scroll bars */
  952.  
  953.     /* Get the scroll bars of the window */
  954.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  955.     vScrollBar = (**pictDocInfo).vScrollBar;
  956.     hScrollBar = (**pictDocInfo).hScrollBar;
  957.  
  958.     /* Show scroll bars if becoming active; hide if becoming inactive */
  959.     if (becomingActive)
  960.     {
  961.         ShowControl( vScrollBar );
  962.         ShowControl( hScrollBar );
  963.     }
  964.     else
  965.     {
  966.         HideControl( vScrollBar );
  967.         HideControl( hScrollBar );
  968.  
  969.         /* To avoid a slight flicker, validate the scroll bar rectangles */
  970.         SetPort( docWindow );
  971.         scrollRect = (**vScrollBar).contrlRect;
  972.         ValidRect( &scrollRect );
  973.         scrollRect = (**hScrollBar).contrlRect;
  974.         ValidRect( &scrollRect );
  975.     }
  976.  
  977.     /* Show or hide the grow icon */
  978.     DrawGrowIcon( docWindow );
  979. }
  980.  
  981.  
  982. /******************************************************************************\
  983. * NAME & SYNOPSIS:
  984. * FixPictDocMenus: Dim, undim, check, or uncheck menus for Picture Documents
  985. *
  986. * PARAMETERS:
  987. * See PictDocument.h
  988. *
  989. * DEFINITION:
  990. * See PictDocument.h
  991. *
  992. * DESCRIPTION:
  993. * The Close and Save As… items in the File menu are enabled when a Picture
  994. * Document window is active, as is the Choose Color… item in the Colors menu.
  995. *
  996. * RETURN VALUES:
  997. * See PictDocument.h
  998. \******************************************************************************/
  999.  
  1000. void FixPictDocMenus(
  1001.     WindowPtr docWindow) /* Pointer to active Picture Document window */
  1002. {
  1003. #pragma unused(docWindow)
  1004.     MenuHandle aMenu; /* Handle to the menus being udpated */
  1005.  
  1006.     /* Set items in the File menu */
  1007.     aMenu = GetMenuHandle( mFile );
  1008.     EnableItem( aMenu, iClose );
  1009.     EnableItem( aMenu, iSaveAs );
  1010. }
  1011.  
  1012.  
  1013. /******************************************************************************\
  1014. * NAME & SYNOPSIS:
  1015. * DoOpenPictDoc: Open a new Picture Document
  1016. *
  1017. * PARAMETERS:
  1018. * See PictDocument.h
  1019. *
  1020. * DEFINITION:
  1021. * See PictDocument.h
  1022. *
  1023. * DESCRIPTION:
  1024. * The usual Standard File dialog is used to get the name and location of the
  1025. * PICT file to open.  Then FindPictDoc (defined earlier in this file) is called
  1026. * to determine whether the chosen PICT file is already open within this
  1027. * application.  If it is, then that PICT file’s window is just selected and
  1028. * DoOpenPictDoc returns with a pointer to that window.
  1029. *
  1030. * If the file isn’t open, then it’s opened here with HOpenDF.  This routine is
  1031. * used because it works even on systems that don’t have the FSSpec-based File
  1032. * Manager calls.  GetPictureRect (defined later in this file) is called to get
  1033. * the picFrame from the picture in the chosen PICT file.  Once this is done,
  1034. * CreatePictDoc is called to create a blank Picture Document window that’s ready
  1035. * to read the image into.
  1036. *
  1037. * The current port is set to the off-screen GrafPort that CreatePictDoc created,
  1038. * and then DrawFilePicture (defined later in this file) is called to spool the
  1039. * PICT file into the current port.
  1040. *
  1041. * RETURN VALUES:
  1042. * See PictDocument.h
  1043. \******************************************************************************/
  1044.  
  1045. WindowPtr DoOpenPictDoc()
  1046. {
  1047.     WindowPtr         pictDocWindow;  /* Pointer to new Picture Doc window */
  1048.     PictDocInfoHnd    pictDocInfo;    /* Handle to Picture Document info rec */
  1049.     PictureInfoRec    pictureInfo;    /* PixMap information from PICT file */
  1050.     Rect              pictureRect;    /* Rectangle of picture in PICT file */
  1051.     CGrafPtr          newImage;       /* New off-screen image */
  1052.     RGBColor          color;          /* Use to set default fore/back colors */
  1053.     CGrafPtr          savedPort;      /* Ptr to current port for restoring */
  1054.     GDHandle          savedDevice;    /* Current GDevice for restoring */
  1055.     StandardFileReply reply;          /* User’s choice for PICT file to open */
  1056.     SFTypeList        typeList;       /* Specifies we can open 'PICT' files */
  1057.     short             pictFileRef;    /* File reference number of PICT file */
  1058.     OSErr             error;
  1059.  
  1060.     
  1061.  
  1062.     pictDocWindow = nil;
  1063.     pictFileRef = 0;
  1064.  
  1065.     /* Let the user choose a PICT file */
  1066.     typeList [0] = 'PICT';
  1067.     if (FileSpecGet( nil, 1, typeList, /*<*/&reply ))
  1068.     {
  1069.         /* First determine whether chosen file is already open in this app */
  1070.         pictDocWindow = FindPictDoc( &reply.sfFile );
  1071.         if (pictDocWindow == nil)
  1072.         {
  1073.             /* Open the PICT file */
  1074.             error = HOpenDF( reply.sfFile.vRefNum, reply.sfFile.parID,
  1075.                     reply.sfFile.name, fsCurPerm, /*<*/&pictFileRef );
  1076.             if (error != noErr)
  1077.                 SysError(error);
  1078.             /* Get interesting information about a picture */
  1079.             error = GetPictureInfo( pictFileRef, /*<*/&pictureInfo );
  1080.             if (error != noErr)
  1081.                 SysError(error);
  1082.             pictureRect = pictureInfo.frame;
  1083.             OffsetRect( /*◊*/&pictureRect, -pictureRect.left,
  1084.                     -pictureRect.top );
  1085.  
  1086.             /* Open a new Picture Document window*/
  1087.             error = CreatePictDoc( &pictureRect, pictureInfo.depth,
  1088.                     pictureInfo.colors, /*<*/&pictDocWindow );
  1089.             if (error != noErr)
  1090.                 SysError(error);
  1091.  
  1092.             /* Fill in the Picture Document information */
  1093.             pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  1094.             color.red = color.green = color.blue = 0x0000;
  1095.             (**pictDocInfo).foreground = color;
  1096.             color.red = color.green = color.blue = 0xFFFF;
  1097.             (**pictDocInfo).background = color;
  1098.             (**pictDocInfo).fileRef = pictFileRef;
  1099.             (**pictDocInfo).file = reply.sfFile;
  1100.             (**pictDocInfo).dithering = false;
  1101.  
  1102.             /* Get a handle to the Picture Document information record */
  1103.             pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  1104.  
  1105.             /* Save current port and set newImage as current port */
  1106.             newImage = (**pictDocInfo).image;
  1107.             GetGWorld( /*<*/&savedPort, /*<*/&savedDevice );
  1108.             SetGWorld( newImage, nil );
  1109.  
  1110.             /* Draw the contents of the PICT file into the current port */
  1111.             error = DrawFilePicture( pictFileRef, &newImage->portRect );
  1112.  
  1113.             /* Restore the current port and check for errors */
  1114.             SetGWorld( savedPort, savedDevice );
  1115.             if (error != noErr)
  1116.                 SysError(error);
  1117.  
  1118.             /* Set the title of the window to the title of the file */
  1119.             SetWTitle( pictDocWindow, reply.sfFile.name );
  1120.  
  1121.             /* Show the window in its final size and position */
  1122.             ShowWindow( pictDocWindow );    
  1123.         }
  1124.         else
  1125.             /* Chosen file already open; just select it */
  1126.             SelectWindow( pictDocWindow );
  1127.     }
  1128.  
  1129.     return pictDocWindow;
  1130. }
  1131.  
  1132.  
  1133. /******************************************************************************\
  1134. * NAME & SYNOPSIS:
  1135. * CreatePictDoc: Create an empty Picture Document
  1136. *
  1137. * PARAMETERS:
  1138. * Rect      *pictureRect: Bounding rectangle of the image
  1139. * WindowPtr *retDoc:      Returns pointer to new Picture Document window
  1140. *
  1141. * DEFINITION:
  1142. * This routine creates a new Picture Document window and returns a pointer to
  1143. * it.  If the Picture Document couldn’t be created for some reason, then
  1144. * CreatePictDoc returns the error code, and the retDoc parameter is untouched.
  1145. *
  1146. * DESCRIPTION:
  1147. * Space for the window record is allocated before GetNewWindow is called so that]
  1148. * heap fragmentation is minimized.  Then, the window is initialized with
  1149. * GetNewWindow.  Then the vertical and horizontal scroll bars are created, and
  1150. * the Picture Document information record is allocated and initialized.  The
  1151. * last major task is to stagger the window into position, and the routines that
  1152. * are defined in WindowPositioner.c are used to do this.
  1153. *
  1154. * RETURN VALUES:
  1155. * Result: Error code if Picture Document window couldn’t be created
  1156. * retDoc: Pointer to the new Picture Document window, 
  1157. \******************************************************************************/
  1158.  
  1159. static OSErr CreatePictDoc(
  1160.     Rect       *pictureRect, /* Bounding rectangle of off-screen image */
  1161.     short      pictureDepth, /* Pixel depth of off-screen image */
  1162.     CTabHandle pictureClut,  /* Color table of off-screen image */
  1163.     WindowPtr  *retDoc)      /* Returns ptr to new Picture Document window */
  1164. {
  1165.     WindowPtr      docWindow;      /* Pointer to new Picture Document window */
  1166.     Ptr            windowStore;    /* Pointer to pre-allocated window rec */
  1167.     PictDocInfoHnd docInfo;        /* Handle to Picture Document information */
  1168.     PaletteHandle  pictureColors;  /* Palette with picture’s colors */
  1169.     GWorldPtr      newImage;       /* New off-screen image */
  1170.     ControlHandle  vScrollBar;     /* Vertical scroll bar */
  1171.     ControlHandle  hScrollBar;     /* Horizontal scroll bar */
  1172.     Rect           windowRect;     /* Destination rectangle of window */
  1173.     Point          windowBias;     /* Bias of Picture Document window */
  1174.     OSErr          error;
  1175.     Point          tempPt;
  1176.  
  1177.     docWindow = nil;
  1178.     docInfo = nil;
  1179.     windowStore = nil;
  1180.  
  1181.     /* Pre-allocate the window record to avoid heap fragmentation */
  1182.     windowStore = NewPtrMargin( sizeof (WindowRecord), kAllocApp, !kAllocClr );
  1183.     if (windowStore == nil)
  1184.         SysError(memFullErr);
  1185.  
  1186.     /* Create the off-screen graphics environment */
  1187.     if (pictureClut == nil)
  1188.         pictureClut = GetCTable( pictureDepth );
  1189.     error = NewGWorld( /*<*/&newImage, pictureDepth, pictureRect, pictureClut,
  1190.             nil, 0 );
  1191.     if (error != noErr)
  1192.         SysError(error);
  1193.  
  1194.     /* Create a new Picture Document window */
  1195.     docWindow = GetNewCWindow( rPictDocWindID, windowStore,
  1196.             (WindowPtr)-1L );
  1197.     if (docWindow == nil)
  1198.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1199.             SysError(memFullErr );
  1200.         else if (ResError() == noErr || ResError() == resNotFound)
  1201.             SysError(resNotFound );
  1202.     SetPort( docWindow );
  1203.  
  1204.     /* Give the window a palette with the window colors */
  1205.     pictureColors = NewPalette( (**pictureClut).ctSize + 1, pictureClut,
  1206.             pmTolerant, 0 );
  1207.     SetPalette( docWindow, pictureColors, true );
  1208.  
  1209.     /* Create the horizontal and vertical scroll bars */
  1210.     vScrollBar = GetNewControl( rScrollBarID, docWindow );
  1211.     if (vScrollBar == nil)
  1212.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1213.             SysError(memFullErr );
  1214.         else if (ResError() == noErr || ResError() == resNotFound)
  1215.             SysError(resNotFound );
  1216.     hScrollBar = GetNewControl( rScrollBarID, docWindow );
  1217.     if (hScrollBar == nil)
  1218.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1219.             SysError(memFullErr );
  1220.         else if (ResError() == noErr || ResError() == resNotFound)
  1221.             SysError(resNotFound );
  1222.  
  1223.     /* Allocate a new Picture Document information record */
  1224.     docInfo = (PictDocInfoHnd)NewHandleMargin( sizeof (PictDocInfoRec),
  1225.             kAllocApp, !kAllocClr );
  1226.     if (docInfo == nil)
  1227.         SysError(memFullErr);
  1228.     (**docInfo).image = newImage;
  1229.     (**docInfo).vScrollBar = vScrollBar;
  1230.     (**docInfo).hScrollBar = hScrollBar;
  1231.     (**docInfo).destRect = newImage->portRect;
  1232.     (**docInfo).fileRef = 0;
  1233.     SetWRefCon( docWindow, (long)docInfo );
  1234.  
  1235.     /* Stagger the window onto the screen */
  1236.     windowBias = CalcWindowBias( documentProc,
  1237.             ((WindowPeek)docWindow)->goAwayFlag );
  1238.     SetRect( &windowRect, 0, 0, pictureRect->right + kScrollBarWidth - 1,
  1239.             pictureRect->bottom + kScrollBarWidth - 1 );
  1240.             
  1241.     /*convert the windowRect to global coordinates */
  1242.     SetPt(&tempPt, windowRect.left, windowRect.top);
  1243.     LocalToGlobal(&tempPt);
  1244.     windowRect.left = tempPt.h;
  1245.     windowRect.top = tempPt.v;
  1246.     SetPt(&tempPt, windowRect.right, windowRect.bottom);
  1247.     LocalToGlobal(&tempPt);
  1248.     windowRect.right = tempPt.h;
  1249.     windowRect.bottom = tempPt.v;
  1250.  
  1251.     PositionScreenRect( &windowRect,
  1252.             kParentScreenPos, kStaggerPos, NextPictDocWindow( nil ),
  1253.             windowBias.h, windowBias.v );
  1254.     MoveWindow( docWindow, windowRect.left, windowRect.top, false );
  1255.     SizeWindow( docWindow, windowRect.right - windowRect.left,
  1256.             windowRect.bottom - windowRect.top, false );
  1257.     ResizePictDoc( docWindow );
  1258.  
  1259.     /* Identify this window for later */
  1260.     ((WindowPeek)docWindow)->windowKind = kDocWKind;
  1261.  
  1262.     *retDoc = docWindow;
  1263.     return noErr;
  1264. }
  1265.  
  1266.  
  1267. /******************************************************************************\
  1268. * NAME & SYNOPSIS:
  1269. * DoSaveAsPictDoc: Save a new Picture Document
  1270. *
  1271. * PARAMETERS:
  1272. * See PictDocument.h
  1273. *
  1274. * DEFINITION:
  1275. * See PictDocument.h
  1276. *
  1277. * DESCRIPTION:
  1278. * The user is asked for a file name for the new Picture Document file through
  1279. * Standard File.  If the user specifies the file name and location and chooses
  1280. * OK, DoSaveAsPictDoc creates a new PICT file if it didn’t exist already, and
  1281. * then it opens the PICT file for writing.  The Picture Document’s off-screen
  1282. * GrafPort is set as the current port, and then OpenFilePicture (defined below)
  1283. * is called which opens a new picture for spooling.  Then the off-screen image
  1284. * is CopyBits’ed on top of itself which spools the off-screen image to the PICT
  1285. * file.  Then the spooled picture is closed, which completes the writing of the
  1286. * PICT file.
  1287. *
  1288. * RETURN VALUES:
  1289. * See PictDocument.h
  1290. \******************************************************************************/
  1291.  
  1292. void DoSaveAsPictDoc(
  1293.     WindowPtr docWindow) /* Pointer to Picture Document window being saved */
  1294. {
  1295.     StandardFileReply reply;       /* Location and name of new PICT file */
  1296.     Str255            docTitle;    /* Holds the existing name of the document */
  1297.     PictDocInfoHnd    docInfo;     /* Handle to Picture Document information */
  1298.     CGrafPtr          image;       /* Points to document’s image */
  1299.     CGrafPtr          savedPort;   /* Saves current port for later restoring */
  1300.     GDHandle          savedDevice; /* Saves current GDevice for restoring */
  1301.     short             pictFileRef; /* File reference number of PICT file */
  1302.     OSErr             error;
  1303.  
  1304.     pictFileRef = 0;
  1305.  
  1306.     /* Get the name and location of the new file from the user */
  1307.     GetWTitle( docWindow, /*<*/docTitle );
  1308.     StandardPutFile( "\p", docTitle, &reply );
  1309.     if (reply.sfGood)
  1310.     {
  1311.         /* Create the new PICT file if it doesn’t already exist */
  1312.         if (!reply.sfReplacing)
  1313.         {
  1314.             error = HCreate( reply.sfFile.vRefNum, reply.sfFile.parID,
  1315.                     reply.sfFile.name, kFileCreator, kFileType );
  1316.             if (error != noErr)
  1317.                 SysError(error);
  1318.         }
  1319.  
  1320.         /* Open the new PICT file */
  1321.         error = HOpen( reply.sfFile.vRefNum, reply.sfFile.parID,
  1322.                 reply.sfFile.name, fsCurPerm, /*<*/&pictFileRef );
  1323.         if (error != noErr)
  1324.             SysError(error);
  1325.         /* Get the Picture Document’s image and make it the current port */
  1326.         docInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  1327.         image = (**docInfo).image;
  1328.         GetGWorld( /*<*/&savedPort, /*<*/&savedDevice );
  1329.         SetGWorld( image, nil );
  1330.  
  1331.         /* Create a PICT file with the Picture Document’s image */
  1332.         error = OpenFilePicture( pictFileRef, &image->portRect );
  1333.         if (error != noErr)
  1334.             SysError(error);
  1335.         CopyBits( &((GrafPtr)image)->portBits, &((GrafPtr)image)->portBits,
  1336.                 &image->portRect, &image->portRect,
  1337.                 srcCopy, nil );
  1338.         error = CloseFilePicture();
  1339.         if (error != noErr)
  1340.             SysError(error);
  1341.  
  1342.         /* Restore the current port */
  1343.         SetGWorld( savedPort, savedDevice );
  1344.  
  1345.         /* Close the existing PICT file if any */
  1346.         if ((**docInfo).fileRef != 0)
  1347.             (void)FSClose( (**docInfo).fileRef );
  1348.  
  1349.         /* Remember the new PICT file’s reference number */
  1350.         (**docInfo).fileRef = pictFileRef;
  1351.         (**docInfo).file = reply.sfFile;
  1352.  
  1353.         /* Set the title of the window to the title of the file */
  1354.         SetWTitle( docWindow, reply.sfFile.name );
  1355.     }
  1356. }
  1357.  
  1358.  
  1359. /******************************************************************************\
  1360. * NAME & SYNOPSIS:
  1361. * DoClosePictDoc: Close a Picture Document window
  1362. *
  1363. * PARAMETERS:
  1364. * See PictDocument.h
  1365. *
  1366. * DEFINITION:
  1367. * See PictDocument.h
  1368. *
  1369. * DESCRIPTION:
  1370. * The off-screen image is disposed of and the Picture Document’s PICT file is
  1371. * closed.  Then all the memory associated with the window is disposed of.
  1372. *
  1373. * RETURN VALUES:
  1374. * See PictDocument.h
  1375. \******************************************************************************/
  1376.  
  1377. Boolean DoClosePictDoc(
  1378.     WindowPtr docWindow) /* Ptr to Picture Document window being closed */
  1379. {
  1380.     PictDocInfoHnd docInfo; /* Handle to Picture Document info record */
  1381.  
  1382.     docInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  1383.     if (docInfo != nil)
  1384.     {
  1385.         /* Get rid of the off-screen CGrafPort */
  1386.         if ((**docInfo).image != nil)
  1387.             DisposeGWorld( (**docInfo).image );
  1388.  
  1389.         /* Close the Picture File */
  1390.         if ((**docInfo).fileRef != 0)
  1391.             (void)FSClose( (**docInfo).fileRef );
  1392.  
  1393.         /* Dispose of the Picture Document information record */
  1394.         DisposeHandle( (Handle)docInfo );
  1395.     }
  1396.  
  1397.     /* Close and dispose of the Picture Document window */
  1398.     DisposePalette( GetPalette( docWindow ) );
  1399.     CloseWindow( docWindow );
  1400.     DisposePtr( (Ptr)docWindow );
  1401.  
  1402.     return true;
  1403. }
  1404.  
  1405.  
  1406. /******************************************************************************\
  1407. * NAME & SYNOPSIS:
  1408. * GetPictureInfo: Get basic information about a PICT document
  1409. *
  1410. * PARAMETERS:
  1411. * short          pictFileRef:  File reference number of PICT file
  1412. * PictureInfoRec *retPictRect: Returns information on picture
  1413. *
  1414. * DEFINITION:
  1415. * GetPictureInfo examines the PICT file whose file reference number is specified
  1416. * in the pictFileRef parameter to get the picFrame of the picture, and the pixel
  1417. * depth and color table of the deepest PixMap in the picture.  This information
  1418. * is returned in the retPictInfo parameter.
  1419. *
  1420. * DESCRIPTION:
  1421. * GetPictureInfo uses picture spooling to get the information about the picture
  1422. * in the specified PICT file.  A temporary CGrafPort or GrafPort is set up just
  1423. * so that no existing ports are touched.  Then a CQDProcs or QDProcs record is
  1424. * set up so that picture data comes from the PICT file instead of memory, and so
  1425. * that any CopyBits calls within the picture get routed to the InfoBits routine
  1426. * (defined below).  Then the standard picture spooling mechanism is used to read
  1427. * the PICT file.  The clip region is set to nothing to prevent drawing——only the
  1428. * information about the PixMaps is needed, and InfoBits gets that.
  1429. *
  1430. * RETURN VALUES:
  1431. * Result:      Error code if anything went wrong
  1432. * retPictInfo: Size of picture, and depth and color table of deepest PixMap in
  1433. *              the picture.
  1434. \******************************************************************************/
  1435.  
  1436. static OSErr GetPictureInfo(
  1437.     short          pictFileRef,  /* File reference number of PICT file */
  1438.     PictureInfoRec *retPictInfo) /* Returns information on picture */
  1439. {
  1440.     CGrafPort    tempPort;     /* Temp port used for “Drawing” pict */
  1441.     Boolean      tempPortOpen; /* True if tempPort has been opened */
  1442.     GrafPtr      savedPort;    /* Saves current port for restoring */
  1443.     CQDProcs     customProcs;  /* Custom GrafProcs */
  1444.     PicHandle    spoolPict;    /* Picture used for spooling */
  1445.     Rect         emptyRect;    /* Used to set empty clipping region */
  1446.     long         dataLen;      /* Number of bytes of data to read */
  1447.     long         qdVersion;    /* Version number of QuickDraw */
  1448.     OSErr        error;
  1449.  
  1450.     error = noErr;
  1451.     tempPortOpen = false;
  1452.     savedPort = nil;
  1453.     spoolPict = nil;
  1454.  
  1455.     /* Get the current version of QuickDraw */
  1456.     (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
  1457.  
  1458.     /* Save current port so that we can restore it later */
  1459.     GetPort( /*<*/&savedPort );
  1460.  
  1461.     /* Open the temporary CGrafPort or GrafPort, depending on QD version */
  1462.     if (qdVersion >= gestalt8BitQD)
  1463.         OpenCPort( &tempPort );
  1464.     else
  1465.         OpenPort( (GrafPtr)&tempPort );
  1466.     tempPortOpen = true;
  1467.  
  1468.     /* Don’t want anything drawn */
  1469.     SetRect( /*<*/&emptyRect, 0, 0, 0, 0 );
  1470.     ClipRect( &emptyRect );
  1471.  
  1472.     /* Set up the GrafProcs for spooling */
  1473.     if (qdVersion >= gestalt8BitQD)
  1474.         SetStdCProcs( /*<*/&customProcs );
  1475.     else
  1476.         SetStdProcs( /*<*/(QDProcsPtr) &customProcs );
  1477.     customProcs.bitsProc = gQDBitsUPP;
  1478.     customProcs.getPicProc = gQDGetPicUPP;
  1479.  
  1480.     /* Install the custom GrafProcs into the tempPort */
  1481.     tempPort.grafProcs = &customProcs;
  1482.  
  1483.     /* Create the picture used for spooling */
  1484.     spoolPict = (PicHandle)NewHandleMargin( sizeof (Picture), kAllocApp,
  1485.             !kAllocClr );
  1486.     if (spoolPict == nil)
  1487.         SysError(memFullErr );
  1488.  
  1489.     /* Skip the PICT file header */
  1490.     error = SetFPos( pictFileRef, fsFromStart, 512L );
  1491.     if (error != noErr)
  1492.         SysError(error);
  1493.  
  1494.     /* Read the picture header into spoolPict */
  1495.     dataLen = sizeof (Picture);
  1496.     error = FSRead( pictFileRef, /*◊*/&dataLen, (Ptr) *spoolPict );
  1497.     if (error != noErr)
  1498.         SysError(error );
  1499.  
  1500.     /* Spool the picture in */
  1501.     gPictureInfo.colors = nil;
  1502.     gPictureInfo.depth = 0;
  1503.     gPictureInfo.frame = (**spoolPict).picFrame;
  1504.     gPictFileRef = pictFileRef;
  1505.     DrawPicture( spoolPict, &gPictureInfo.frame );
  1506.  
  1507.     /* Clean up after myself */
  1508.     SetPort( savedPort );
  1509.     ClosePort( (GrafPtr)&tempPort );
  1510.     tempPortOpen = false;
  1511.     DisposeHandle( (Handle)spoolPict );
  1512.  
  1513.     /* Return picture information */
  1514.     *retPictInfo = gPictureInfo;
  1515.     return noErr;
  1516.     }
  1517.  
  1518.  
  1519. /******************************************************************************\
  1520. * NAME & SYNOPSIS:
  1521. * InfoBits: GrafProc to get information about bit/pixel maps
  1522. *
  1523. * PARAMETERS:
  1524. * BitMapPtr srcBits:  Pointer to bit/pixel map being transferred
  1525. * Rect      *srcRect: Source rectangle of CopyBits call; ignored
  1526. * Rect      *dstRect: Destination rectangle of CopyBits call; ignored
  1527. * short     mode:     Transfer mode; ignored
  1528. * RgnHandle maskRgn:  Region to mask CopyBits; ignored
  1529. *
  1530. * DEFINITION:
  1531. * InfoBits is a custom QuickDraw graphics procedure which is used to find the
  1532. * depth and color table of the deepest PixMap in the picture that’s being drawn.
  1533. * This information is returned in the gPictureInfo global variable.
  1534. *
  1535. * DESCRIPTION:
  1536. * InfoBits is called every time a CopyBits call is found in a picture that’s
  1537. * being drawn.  It first checks to see if the CopyBits call is for a PixMap or
  1538. * a BitMap.  If it’s a BitMap, the most significant bit of rowBytes is clear; if
  1539. * it’s a PixMap, the most significant byte of rowBytes is set.  If it’s a
  1540. * PixMap, its depth is compared against the depth of any previous PixMap that
  1541. * has been seen in the picture.  If it’s deeper, then the depth and color table
  1542. * of the PixMap is saved.  Otherwise, the PixMap is ignored.
  1543. *
  1544. * If srcBits is a BitMap and no PixMaps have yet been seen in the picture, then
  1545. * InfoBits remembers a depth of 1 bit per pixel and no color table.
  1546. *
  1547. * RETURN VALUES:
  1548. * gPictureInfo: Depth and color table of deepest PixMap in a picture.
  1549. \******************************************************************************/
  1550.  
  1551. pascal void InfoBits(
  1552.     BitMapPtr srcBits,  /* Pointer to bit/pixel map being transferred */
  1553.     Rect      *srcRect, /* Source rectangle of CopyBits call; ignored */
  1554.     Rect      *dstRect, /* Destination rectangle of CopyBits call; ignored */
  1555.     short     mode,     /* Transfer mode; ignored */
  1556.     RgnHandle maskRgn)  /* Region to mask CopyBits; ignored */
  1557. {
  1558. #pragma unused (srcRect,dstRect,mode,maskRgn)
  1559.     PixMapPtr srcPix; /* Handle to the picture’s PixMap */
  1560.  
  1561.     if (srcBits->rowBytes & 0x8000)
  1562.     {
  1563.         /* srcBits is a pixel map, get depth and color table */
  1564.         srcPix = (PixMapPtr)srcBits;
  1565.  
  1566.         /* Only save info if this pixel map is deepest seen so far */
  1567.         if (srcPix->pixelSize > gPictureInfo.depth)
  1568.         {
  1569.             gPictureInfo.depth = srcPix->pixelSize;
  1570.             if (srcPix->pixelType != RGBDirect)
  1571.             {
  1572.                 if (gPictureInfo.colors != nil)
  1573.                     DisposeHandle( (Handle)gPictureInfo.colors );
  1574.                 gPictureInfo.colors = srcPix->pmTable;
  1575.                 HandToHand( (Handle *)&gPictureInfo.colors );
  1576.             }
  1577.         }
  1578.     }
  1579.     else
  1580.     {
  1581.         /* srcBits is a bit map, has depth of 1 and B&W color table */
  1582.         if (gPictureInfo.depth == 0)
  1583.         {
  1584.             gPictureInfo.depth = 1;
  1585.             if (gPictureInfo.colors != nil)
  1586.                 DisposeHandle( (Handle)gPictureInfo.colors );
  1587.             gPictureInfo.colors = nil;
  1588.         }
  1589.     }
  1590. }
  1591.  
  1592.  
  1593. /******************************************************************************\
  1594. * NAME & SYNOPSIS:
  1595. * DrawFilePicture: Read a PICT file into the current GrafPort
  1596. *
  1597. * PARAMETERS:
  1598. * short pictFileRef: File reference number of PICT file to open
  1599. * Rect  *destRect:   Rectangle to draw the picture into
  1600. *
  1601. * DEFINITION:
  1602. * This routine is used to read the picture data from the PICT file specified by
  1603. * pictFileRef and draw the picture into the rectangle specified by the destRect
  1604. * parameter in the current GrafPort.  It’s just like DrawPicture, except the
  1605. * file reference number of a PICT file is specified instead of a PicHandle.
  1606. *
  1607. * DESCRIPTION:
  1608. * DrawFilePicture works by using the picture spooling method described in Inside
  1609. * Macintosh V on pages 87 through 90.  In this method, the getPicProc GrafProc is
  1610. * replaced by my routine called FileGetPic which reads PICT information from
  1611. * disk.  By doing this, the DrawPicture command effectively reads PICT files.
  1612. *
  1613. * After setting up the GrafProcs record, this routine installs the GrafProcs
  1614. * into the current port.  Then, a temporary picture is manually allocated and
  1615. * placed into spoolPict.  This is essentially a placeholder to hold the picture
  1616. * header (which is different from the 512-byte PICT file header) and is always
  1617. * the size of the Picture record type without any picture data.
  1618. *
  1619. * The PICT file is opened using HOpen.  HOpen is documented in the File Manager
  1620. * chapter of Inside Macintosh VI, but it still works on pre-7.0 machines because
  1621. * it’s just glue code that calls PBHOpen.  Because of the way the File Manager
  1622. * works, this routine also works on MFS volumes.  This routine is preferable to
  1623. * FSOpen because HOpen allows you to specify access permissions, and because it
  1624. * lets you specify a file by the more reliable volume reference number,
  1625. * directory ID, and file name rather than by working directory reference number
  1626. * and file name.  I’m using the FSSpec record to hold file information, so
  1627. * specifying a file like this is especially convenient.  If this were a 7.0-only
  1628. * application, I’d call FSpOpen instead of HOpen because FSpOpen takes an FSSpec
  1629. * record directly.
  1630. *
  1631. * After the PICT file is opened, SetFPos is called to skip the first 512 bytes
  1632. * of the file.  The first 512 bytes of a PICT file is called the PICT file
  1633. * header (not to be confused with the picture header, which contains the picSize
  1634. * and picFrame fields of the Picture type) and holds application-specific
  1635. * information.  This application doesn’t care what’s there so it just skips it.
  1636. * The picture header follows the PICT file header and is read into spoolPict.
  1637. *
  1638. * Finally (almost), DrawPicture is called.  Because this routine replaced the
  1639. * current port’s GrafProcs with my own, DrawPicture gets the picture information
  1640. * from the PICT file rather than from spoolPict.  It draws the picture into the
  1641. * current port.
  1642. *
  1643. * RETURN VALUES:
  1644. * Result:   Returns an error code.
  1645. * destRect: Returns the picFrame of the picture in the PICT file
  1646. \******************************************************************************/
  1647.  
  1648. static OSErr DrawFilePicture(
  1649.     short pictFileRef, /* File reference number of PICT file to open */
  1650.     Rect  *destRect)   /* Rectangle to draw the picture into */
  1651. {
  1652.     GrafPtr      currPort;    /* Ptr to current port used for Drawing pict */
  1653.     CQDProcs     customProcs; /* Custom GrafProcs */
  1654.     QDProcsPtr   savedProcs;  /* Current GrafPort’s QD procs; used to restore */
  1655.     PicHandle    spoolPict;   /* Picture used for spooling */
  1656.     long         dataLen;     /* Number of bytes of data to read */
  1657.     long         qdVersion;   /* Version number of QuickDraw */
  1658.     OSErr        error;
  1659.  
  1660.     currPort = nil;
  1661.     spoolPict = nil;
  1662.     savedProcs = nil;
  1663.     error = noErr;
  1664.  
  1665.     /* Get the current version of QuickDraw */
  1666.     (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
  1667.  
  1668.     /* Get a pointer to the current port and determine its type */
  1669.     GetPort( /*<*/&currPort );
  1670.  
  1671.     /* Initialize the GrafProcs for reading from a PICT file */
  1672.     if (qdVersion >= gestalt8BitQD)
  1673.         SetStdCProcs( /*<*/&customProcs );
  1674.     else
  1675.         SetStdProcs( /*<*/(QDProcsPtr) &customProcs );
  1676.     customProcs.getPicProc = gQDGetPicUPP;
  1677.  
  1678.     /* Install the custom GrafProcs into the tempPort */
  1679.     savedProcs = currPort->grafProcs;
  1680.     currPort->grafProcs = (QDProcsPtr)&customProcs;
  1681.  
  1682.     /* Create the temporary picture used for spooling */
  1683.     spoolPict = (PicHandle)NewHandleMargin( sizeof (Picture),
  1684.             kAllocApp, !kAllocClr );
  1685.     if (spoolPict == nil)
  1686.         SysError(memFullErr );
  1687.  
  1688.     /* Skip the 512-byte PICT file header */
  1689.     error = SetFPos( pictFileRef, fsFromStart, kPictFileHeaderSize );
  1690.     if (error != noErr)
  1691.         SysError(error );
  1692.  
  1693.     /* Read the picture header (not PICT file header) into spoolPict */
  1694.     dataLen = sizeof (Picture);
  1695.     error = FSRead( pictFileRef, /*◊*/&dataLen, (Ptr)*spoolPict );
  1696.     if (error != noErr)
  1697.         SysError(error );
  1698.  
  1699.     /* Spool the picture in */
  1700.     gPictFileRef = pictFileRef;
  1701.     DrawPicture( spoolPict, destRect );
  1702.  
  1703.     /* Clean up after myself */
  1704.     DisposeHandle( (Handle)spoolPict );
  1705.     currPort->grafProcs = savedProcs;
  1706.  
  1707.     return noErr;
  1708. }
  1709.  
  1710.  
  1711. /******************************************************************************\
  1712. * NAME & SYNOPSIS:
  1713. * FileGetPic: Get QuickDraw picture information from a file
  1714. *
  1715. * PARAMETERS:
  1716. * Ptr   dataPtr:   Points to location that picture data should be placed
  1717. * short byteCount: Number of bytes of picture data to read
  1718. *
  1719. * DEFINITION:
  1720. * This routine is a custom QuickDraw graphics procedure which replaces the
  1721. * standard GetPicProc.  QuickDraw graphics procedures are routines that
  1722. * QuickDraw calls to do almost everything that it does, like drawing rectangles,
  1723. * or getting picture data.  They can be replaced by setting up a QDProcs record
  1724. * and setting the appropriate field to point to the custom QuickDraw graphics
  1725. * procedure.  In this case, the getPicProc field is set to point to FileGetPic
  1726. * so that any DrawPicture call within the GrafPort causes FileGetPic to be
  1727. * called to get picture data.  Instead of getting picture data from memory,
  1728. * FileGetPic gets picture data from the disk file whose file reference number is
  1729. * stored in the gPictFileRef global variable.
  1730. *
  1731. * DESCRIPTION:
  1732. * FileGetPic simply calls FSRead to read data from the file whose file reference
  1733. * number is stored in gPictFileRef into the area pointed to by dataPtr, which is
  1734. * an area of memory that’s set up by QuickDraw to receive picture data.  The
  1735. * number of bytes of picture data that QuickDraw wants is specified by the
  1736. * byteCount parameter.
  1737. *
  1738. * RETURN VALUES:
  1739. * None
  1740. \******************************************************************************/
  1741.  
  1742. pascal void FileGetPic(
  1743.     Ptr   dataPtr,   /* Points to location that picture data should be placed */
  1744.     short byteCount) /* Number of bytes of picture data to read */
  1745. {
  1746.     long longCount; /* Number of bytes to read from the PICT file */
  1747.  
  1748.     longCount = byteCount;
  1749.     (void)FSRead( gPictFileRef, /*◊*/&longCount, dataPtr );
  1750. }
  1751.  
  1752.  
  1753. /******************************************************************************\
  1754. * NAME & SYNOPSIS:
  1755. * OpenFilePicture: Open a picture for spooling to a file
  1756. *
  1757. * PARAMETERS:
  1758. * short pictFileRef: File reference number of PICT file to write to
  1759. * Rect  *sourceRect: Rectangle to draw the picture from
  1760. *
  1761. * DEFINITION:
  1762. * Normally, when you create a picture, you call OpenPicture, draw, then call
  1763. * ClosePicture.  But if you can create a PICT file in a similar way by calling
  1764. * OpenFilePicture and CloseFilePicture.  Almost like you would normally, you
  1765. * should set up the current port however you want it, call OpenFilePicture,
  1766. * use QuickDraw routines to draw, then call CloseFilePicture when you’re done.
  1767. * But before you call OpenFilePicture, you must open the PICT file for writing,
  1768. * and pass the file’s reference number in the pictFileRef parameter.  Once
  1769. * CloseFilePicture is called, the PICT file is complete.
  1770. *
  1771. * DESCRIPTION:
  1772. * OpenFilePicture installs the FilePutPic custom graphics procedure by setting
  1773. * up a QDprocs record with the default pointers, and then setting the putPicProc
  1774. * field to point to the FilePutPic function.  This QDProcs record is then
  1775. * installed into the current GrafPort.  Then, an empty picture is allocated just
  1776. * to have something to pass to the real OpenPicture routine.  Then the 512-byte
  1777. * PICT file header is written.  This program doesn’t do anything with those 512
  1778. * bytes, so only zeroes are written.  Also, the ten-byte picture header is
  1779. * written with zeroes for now; it’ll get the real picture header after the PICT
  1780. * file data is written.  Finally, the global variables are set up so that
  1781. * FilePutPic can reach them.
  1782. *
  1783. * RETURN VALUES:
  1784. * Result: Error code of any error that occurs.
  1785. \******************************************************************************/
  1786.  
  1787. static OSErr OpenFilePicture(
  1788.     short pictFileRef, /* File reference number of PICT file to open */
  1789.     Rect  *sourceRect) /* Rectangle to draw the picture from */
  1790. {
  1791.     GrafPtr      currPort;    /* Ptr to current port used for Drawing pict */
  1792.     long         dataLen;     /* Number of bytes of data to write */
  1793.     short        zeroData;    /* Equal to 0; used to write PICT file header */
  1794.     short        count;       /* Generic counter */
  1795.     OSErr        error;
  1796.     currPort = nil;
  1797.     gSpoolPicture = nil;
  1798.     gSavedProcs = nil;
  1799.     error = noErr;
  1800.  
  1801.     /* Get a pointer to the current port */
  1802.     GetPort( /*<*/&currPort );
  1803.  
  1804.     /* Initialize the GrafProcs for reading from a PICT file */
  1805.     if (currPort->portBits.rowBytes & 0x8000)
  1806.         SetStdCProcs( /*<*/&gCustomProcs );
  1807.     else
  1808.         SetStdProcs( /*<*/(QDProcsPtr)&gCustomProcs );
  1809.     gCustomProcs.putPicProc = gQDPutPicUPP;
  1810.  
  1811.     /* Install the custom GrafProcs into the tempPort */
  1812.     gSavedProcs = currPort->grafProcs;
  1813.     currPort->grafProcs = (QDProcsPtr)&gCustomProcs;
  1814.  
  1815.     /* Create the temporary picture used for spooling */
  1816.     gSpoolPicture = (PicHandle)NewHandleMargin( sizeof (Picture),
  1817.             kAllocApp, kAllocClr );
  1818.     if (gSpoolPicture == nil)
  1819.         SysError(memFullErr );
  1820.  
  1821.     /* Make sure we’re positioned at the beginning of the file */
  1822.     error = SetFPos( pictFileRef, fsFromStart, 0 );
  1823.     if (error != noErr)
  1824.         SysError( error );
  1825.  
  1826.     /* Zero the 512-byte PICT file header */
  1827.     dataLen = sizeof (short);
  1828.     zeroData = 0;
  1829.     for (count = 0; count < (kPictFileHeaderSize + sizeof (Picture))
  1830.             / sizeof (short); ++count)
  1831.     {
  1832.         error = FSWrite( pictFileRef, /*◊*/&dataLen, &zeroData );
  1833.         if (error != noErr)
  1834.             SysError(error );
  1835.     }
  1836.  
  1837.     /* Begin spooling the picture out */
  1838.     gPictFileRef = pictFileRef;
  1839.     gPictureSize = sizeof (Picture);
  1840.     gSpoolPicture = nil;
  1841.     gSpoolPicture = OpenPicture( sourceRect );
  1842.  
  1843.     return noErr;
  1844. }
  1845.  
  1846.  
  1847. /******************************************************************************\
  1848. * NAME & SYNOPSIS:
  1849. * CloseFilePicture: Close a spooled picture (don’t close the PICT file)
  1850. *
  1851. * PARAMETERS:
  1852. * None
  1853. *
  1854. * DEFINITION:
  1855. * ClosePictureFile is called to complete the writing of a PICT file.  It’s
  1856. * called the same way you’d call ClosePicture.
  1857. *
  1858. * DESCRIPTION:
  1859. * ClosePicture is called to close the currently-open picture, then the picture
  1860. * header is written with the final values.  Because the temporary picture isn’t
  1861. * needed anymore, KillPicture is called to get rid of it.  Finally, the current
  1862. * port’s graphics procedures are restored to what they were before
  1863. * OpenFilePicture was called.
  1864. *
  1865. * RETURN VALUES:
  1866. * Result: Error code of any error that occurs.
  1867. \******************************************************************************/
  1868.  
  1869. static OSErr CloseFilePicture()
  1870. {
  1871.     long         dataLen;  /* Number of bytes of data to write */
  1872.     GrafPtr      currPort; /* Pointer to the current GrafPort */
  1873.     OSErr        error;
  1874.  
  1875.     error = noErr;
  1876.  
  1877.     /* Close the spooled picture */
  1878.     ClosePicture();
  1879.  
  1880.     /* Make sure we’re positioned just past the PICT file header */
  1881.     error = SetFPos( gPictFileRef, fsFromStart, kPictFileHeaderSize );
  1882.     if (error != noErr)
  1883.         SysError(error );
  1884.  
  1885.     /* Write the picture header (NOT the PICT file header!) */
  1886.     dataLen = sizeof (Picture);
  1887.     error = FSWrite( gPictFileRef, /*◊*/&dataLen, (Ptr)*gSpoolPicture );
  1888.     if (error != noErr)
  1889.         SysError(error );
  1890.  
  1891.     /* Dispose of the picture */
  1892.     KillPicture( gSpoolPicture );
  1893.  
  1894.     /* Restore the previous graphics procedures */
  1895.     GetPort( /*<*/&currPort);
  1896.     currPort->grafProcs = gSavedProcs;
  1897.     return noErr;
  1898. }
  1899.  
  1900.  
  1901. /******************************************************************************\
  1902. * NAME & SYNOPSIS:
  1903. * FilePutPic: Write QuickDraw picture information to PICT file
  1904. *
  1905. * PARAMETERS:
  1906. * Ptr   dataPtr:   Points to location of picture data
  1907. * short byteCount: Number of bytes of picture data to write
  1908. *
  1909. * DEFINITION:
  1910. * This routine is a custom QuickDraw graphics procedure which replaces the
  1911. * standard PutPicProc.  QuickDraw graphics procedures are routines that
  1912. * QuickDraw calls to do almost everything that it does, like drawing rectangles,
  1913. * or getting picture data.  They can be replaced by setting up a QDProcs record
  1914. * and setting the appropriate field to point to the custom QuickDraw graphics
  1915. * procedure.  In this case, the getPicProc field is set to point to FileGetPic
  1916. * so that any drawing call within the GrafPort while a picture is open causes
  1917. * FilePutPic to be called to write picture data.  Instead of writing picture
  1918. * data to memory, FileGetPic writes picture data to the disk file whose file
  1919. * reference number is stored in the gPictFileRef global variable.
  1920. *
  1921. * DESCRIPTION:
  1922. * FileGetPic calls FSWrite to write data t=o the file whose file reference
  1923. * number is stored in gPictFileRef from the area pointed to by dataPtr, which is
  1924. * an area of memory that’s set up by QuickDraw to hold picture data.  The
  1925. * number of bytes of picture data that QuickDraw has is specified by the
  1926. * byteCount parameter.  Also, the picSize field of gSpoolPicture is updated so
  1927. * that QuickDraw can keep track of the current size of the picture.
  1928. *
  1929. * RETURN VALUES:
  1930. * None
  1931. \******************************************************************************/
  1932.  
  1933. pascal void FilePutPic(
  1934.     Ptr   dataPtr,   /* Points to location of picture data */
  1935.     short byteCount) /* Number of bytes of picture data to write */
  1936. {
  1937.     long dataLen; /* Number of bytes to write the PICT file */
  1938.  
  1939.     dataLen = byteCount;
  1940.     gPictureSize += dataLen;
  1941.     (void)FSWrite( gPictFileRef, /*◊*/&dataLen, dataPtr );
  1942.     if (gSpoolPicture != nil)
  1943.         (**gSpoolPicture).picSize = gPictureSize;
  1944. }
  1945.